#include "my_switch_button.h"


/*
 * Function:    MySwitchButton
 * Description: The default constructor
 * Paramer:     void
 * Return:
 */
MySwitchButton::MySwitchButton(QWidget *parent)
    : QWidget{parent}
{
}

/*
 * MySwitchButton:    ~MySwitchButton
 * Description: The default destructor
 * Paramer:     void
 * Return:
 */
MySwitchButton::~MySwitchButton()
{
}

/*
 * Function:    getSwitchStatus
 * Description: Get the value of switch button
 * Paramer:     void
 * Return:      bool - true: ON; otherwise: OFF
 */
bool MySwitchButton::getSwitchValue(){
    return m_bValueOnOff;
}

/*
 * Function:    getEnableState
 * Description: Get the enable state of switch button
 * Paramer:     void
 * Return:      bool - true: ON; otherwise: OFF
 */
bool MySwitchButton::getEnableState(){
    return m_bEnableState;
}

/*
 * Function:    setSwitchValue
 * Description: Set the value of switch button
 * Paramer:     bool bValue - value of button
 * Return:      void
 */
void MySwitchButton::setSwitchValue(bool bValue, bool bNotifyChanged){
    if(m_bWaitModelForSignal) return;

    m_bValueOnOff = bValue;

    if(bNotifyChanged) {
        // The value of the switch changes
        emit signalValueChanged(m_bValueOnOff);
    }

    // Animation of background color
    QPropertyAnimation *pAnimationBgColor = new QPropertyAnimation(this, "clrBackground");
    pAnimationBgColor->setDuration(m_nAnimationPeriod);
    pAnimationBgColor->setStartValue(m_clrBackground);
    pAnimationBgColor->setEndValue(m_bValueOnOff ?  m_clrBackgroundON: m_clrBackgroundOFF);
    pAnimationBgColor->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);

    // Animation of switch position
    QVariantAnimation  *pAnimationSwitchPos = new QVariantAnimation(this);

    connect(pAnimationSwitchPos, &QPropertyAnimation::valueChanged, [=](const QVariant &nValue){
        m_recButton.moveTo(nValue.toPointF());
        update();
    });

    pAnimationSwitchPos->setDuration(m_nAnimationPeriod);
    pAnimationSwitchPos->setStartValue(m_recButton.topLeft());
    pAnimationSwitchPos->setEndValue(m_bValueOnOff ?  m_pntRightON : m_pntLeftOFF);
    pAnimationSwitchPos->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);
}

/*
 * Function:    setEnableState
 * Description: Set the enable state of switch button
 * Paramer:     bool bValue - enabling state of button
 * Return:      void
 */
void MySwitchButton::setEnableState(bool bEnable){
    m_bEnableState = bEnable;

    emit signalEnableStateChanged(m_bEnableState);

    QWidget::setEnabled(bEnable);
    update();
}

/*
 * Function:    setAnimationPeriod
 * Description: Set the values of animation period
 * Paramer:     int nPeriod - values of animation period
 * Return:      void
 */
void MySwitchButton::setAnimationPeriod(int nPeriod){
    m_nAnimationPeriod = nPeriod;
}

/*
 * Function:    setSwitchUnderWaitModel
 * Description: Set the values of switch under wait model
 * Paramer:     bool bSwitchValue - values of switch
 * Return:      void
 */
void MySwitchButton::setSwitchUnderWaitModel(bool bSwitchValue)
{
    if(!m_bWaitModelForSignal) return;

    if(m_bValueOnOff == bSwitchValue){
        // If the switch value is not changed, change the position of the embedded button first
        QVariantAnimation *pAnimationSwitchPos = new QVariantAnimation(this);

        connect(pAnimationSwitchPos, &QVariantAnimation::valueChanged, [=](const QVariant &value){
            m_recButton.moveTo(value.toPointF());
            update();
        });

        pAnimationSwitchPos->setDuration(m_nAnimationPeriod);
        pAnimationSwitchPos->setStartValue(m_bValueOnOff ? m_pntLeftOFF : m_pntRightON);
        pAnimationSwitchPos->setEndValue(m_bValueOnOff ?  m_pntRightON : m_pntLeftOFF);
        pAnimationSwitchPos->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);

        return ;
    }

    m_bValueOnOff = bSwitchValue;

    // The value of the switch changes
    signalValueChanged(m_bValueOnOff);

    // Change the background color of the switch
    QPropertyAnimation * pAnimationBgColor = new QPropertyAnimation(this,"clrBackground");

    connect(pAnimationBgColor, &QPropertyAnimation::valueChanged, [=](const QVariant &value){
        m_recButton.moveTo(value.toPointF());
        update();
    });

    pAnimationBgColor->setDuration(m_nAnimationPeriod);
    pAnimationBgColor->setStartValue(m_clrBackground);
    pAnimationBgColor->setEndValue(m_bValueOnOff ?  m_clrBackgroundON: m_clrBackgroundOFF);
    pAnimationBgColor->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);
}

/*
 * Function:    setPrecisionClick
 * Description: Set the precise click mark of button embedded in switch
 * Paramer:     bool bFlag - is precise click mark?
 * Return:      void
 */
void MySwitchButton::setPrecisionClick(bool bClickFlag){
    m_bPrecisionClick = bClickFlag;
}

/*
 * Function:    setWaitModel
 * Description: Set wait model (You need to call "setSwitch" function before switching the value of the switch)
 * Paramer:     bool bWaitModel -  is wait model?
 * Return:      void
 */
void MySwitchButton::setWaitModel(bool bWaitModel)
{
    m_bWaitModelForSignal = bWaitModel;
}

/*
 * Function:    setInnerButtonColor
 * Description: Sets the color of the embedded controls for the switch
 * Paramer:     QColor clrButton - the color of the embedded controls
 * Return:      void
 */
void MySwitchButton::setInnerButtonColor(QColor clrButton){
    m_clrButton = clrButton;
    update();
}

/*
 * Function:    setBackgroundColorON
 * Description: Set the background color in the ON state
 * Paramer:     QColor clrBackground - the background color of switch
 * Return:      void
 */
void MySwitchButton::setBackgroundColorON(QColor clrBackground){
    m_clrBackgroundON = clrBackground;
    update();
}

/*
 * Function:    setBackgroundColorOFF
 * Description: Set the background color in the OFF state
 * Paramer:     QColor clrBackground - the background color of switch
 * Return:      void
 */
void MySwitchButton::setBackgroundColorOFF(QColor clrBackground){
    m_clrBackgroundOFF = clrBackground;
    update();
}

/*
 * Function:    setEmbeddedEdgeColor
 * Description: Set the edge color of the embedded control
 * Paramer:     QColor clrEdge - the edge color of the embedded control
 * Return:      void
 */
void MySwitchButton::setEmbeddedEdgeColor(QColor clrEdge){
    m_clrButtonEdge = clrEdge;
    update();
}

void MySwitchButton::paintEvent(QPaintEvent *event){
    Q_UNUSED(event)

    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing, true);
    painter.setPen(Qt::NoPen);

    // Draw switch edge color
    QPainterPath path;
    path.addRoundedRect(this->rect(), m_nBorderRadius, m_nBorderRadius);
    path.setFillRule(Qt::OddEvenFill);
    painter.setBrush(m_clrButtonEdge);
    painter.drawPath(path);

    // Draw switch background color
    painter.setBrush(m_clrBackground);
    painter.drawRoundedRect(this->rect(), 4, 4);

    // Draw an embedded button
    painter.setBrush(m_clrButton);
    painter.drawRoundRect(m_recButton);

    // Draw shadows for embedded buttons
    painter.setBrush(Qt::NoBrush);
    QColor clrShadow(Qt::black);
    int nCount = (this->height() - m_recButton.height())/2;
    float fStepColor = (0.15-0.0) / nCount;

    for (int i = m_recButton.height()/2 + 1; i < this->height()/2; i++){
        clrShadow.setAlphaF(0.15 - fStepColor * (i - m_recButton.height()/2));

        QFont font = QApplication::font("QLabel");
        font.setPixelSize(16);
        painter.setFont(font);
        painter.setPen(m_bValueOnOff ? QColor(Qt::gray) : clrShadow);
//        painter.drawEllipse(m_recButton.center(), i, i);

        QString sText = m_bValueOnOff ? tr("ON") : tr("OFF");
        QFontMetrics fontMetrics(font);
        int nBoundWidth = fontMetrics.boundingRect(sText).width();
        int nBoundHeight = fontMetrics.boundingRect(sText).height();
        QPoint pntLeft(m_recButton.center().x() - nBoundWidth / 2, m_recButton.center().y() + nBoundHeight / 3);

        painter.drawText(pntLeft, sText);
    }

    // Add a mask
    if(!m_bEnableState){
        QColor clrDisable(Qt::black);
        clrDisable.setAlphaF(0.3);
        painter.setBrush(clrDisable);
//        painter.drawRoundedRect(this->rect(), m_nBorderRadius, m_nBorderRadius);
        painter.drawRoundRect(this->rect());
    }
}

void MySwitchButton::resizeEvent(QResizeEvent *event){
    Q_UNUSED(event)

    // Update button size, fillet size and animation
    int nSize = qMin(this->width(), this->height());
    m_nBorderRadius = nSize / 2;

    float fWidth = nSize * 3 / 4;
    float fBorder = (nSize - fWidth) / 2;

    m_pntLeftOFF = QPoint(fBorder, fBorder);
    m_pntRightON = QPoint(this->width() - fBorder - fWidth * 1.5, fBorder);
    m_recButton.setWidth(fWidth * 1.5);
    m_recButton.setHeight(fWidth);
    m_recButton.moveTo(m_bValueOnOff ? m_pntRightON : m_pntLeftOFF);
    m_clrBackground = m_bValueOnOff ? m_clrBackgroundON : m_clrBackgroundOFF ;

    update();
}

void MySwitchButton::mouseReleaseEvent(QMouseEvent *event){
    if(m_bWaitModelForSignal){
        // change the position of the embedded button first
        QVariantAnimation  *pAnimationSwitchPos = new QVariantAnimation(this);

        connect(pAnimationSwitchPos, &QVariantAnimation::valueChanged, [=](const QVariant &value){
            m_recButton.moveTo(value.toPointF());
            update();
        });

        pAnimationSwitchPos->setDuration(m_nAnimationPeriod);
        pAnimationSwitchPos->setStartValue(m_bValueOnOff ? m_pntRightON : m_pntLeftOFF);
        pAnimationSwitchPos->setEndValue(m_bValueOnOff ?  m_pntLeftOFF : m_pntRightON);
        pAnimationSwitchPos->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);

        return ;
    }

    if(!m_bEnableState) return;

    if(!m_recButton.contains(event->pos()) && !m_bPrecisionClick){
        // Expand the click range of embedded buttons
        int nSize = qMin(this->width(), this->height());
        QRectF m_recButtonEx(m_recButton);
        m_recButtonEx.setWidth(nSize);
        m_recButtonEx.setHeight(nSize);

        if(!m_recButtonEx.contains(event->pos())) {
            // Response to click event outside the scope of embedded button
            setSwitchValue(!m_bValueOnOff);
        }
    }
    else if(m_recButton.contains(event->pos()) && m_bPrecisionClick) {
        // Response to click event within the scope of embedded button
        setSwitchValue(!m_bValueOnOff);
    }
}

void MySwitchButton::enterEvent(QEvent *event){
    Q_UNUSED(event)
    m_bMouseHover = true;
}

void MySwitchButton::leaveEvent(QEvent *event){
    Q_UNUSED(event)
    m_bMouseHover = false;
}

